package club.xyes.zkh.ecommerce.web.api.interceptor;

import club.xyes.zkh.ecommerce.commons.context.ApplicationConstants;
import club.xyes.zkh.ecommerce.commons.dto.MinimalShopInfo;
import club.xyes.zkh.ecommerce.commons.dto.MinimalUserInfo;
import club.xyes.zkh.ecommerce.commons.http.holder.RequestExtendParamHolder;
import club.xyes.zkh.ecommerce.commons.vo.GeneralResult;
import club.xyes.zkh.ecommerce.security.encoder.AccessTokenEncoder;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.PathMatcher;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * Create by 郭文梁 2019/4/19 0019 09:33
 * AccessTokenInterceptor
 * 访问令牌拦截器
 *
 * @author 郭文梁
 * @data 2019/4/19 0019
 */
@Slf4j
public class AccessTokenInterceptor implements HandlerInterceptor {
    private final PathMatcher matcher;
    private final AccessTokenEncoder encoder;

    public AccessTokenInterceptor(PathMatcher matcher, AccessTokenEncoder encoder) {
        this.matcher = matcher;
        this.encoder = encoder;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        final String path = request.getRequestURI();
        if (matcher.match(ApplicationConstants.Security.API_BASE_URL, path)
                && !matcher.match(ApplicationConstants.Security.USER_API_LOGIN_URL, path)
                && !matcher.match(ApplicationConstants.Security.SHOP_API_LOGIN_URL, path)
                && !matcher.match("/api/user/*/token", path)
                && !matcher.match("/api/shop/*/token", path)
                && !matcher.match("/api/pay/notify", path)
                && !matcher.match("/api/test/**", path)
                && !matcher.match("/api/user-access-shop-log/shop/*/export", path)) {
            String accessToken = request.getHeader(ApplicationConstants.Security.API_ACCESS_TOKEN_HEADER);
            log.debug("Path [{}] Header[Api-Access-Token] is [{}]", path, accessToken);
            MinimalUserInfo userInfo = obtainUserInfo(accessToken);
            MinimalShopInfo shopInfo = obtainShopInfo(accessToken);
            if (userInfo != null || shopInfo != null) {
                setUserInfo(userInfo);
                setShopInfo(shopInfo);
                return true;
            } else {
                String content = JSON.toJSONString(GeneralResult.permissionDenied());
                sendJSON(response, content);
                return false;
            }
        } else {
            return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
        RequestExtendParamHolder.clear();
    }

    private void sendJSON(HttpServletResponse response, String content) throws IOException {
        response.setCharacterEncoding(ApplicationConstants.DEFAULT_CHARSET);
        response.setContentType(ApplicationConstants.Http.CONTENT_TYPE_JSON_UTF8);
        response.getWriter().write(content);
    }

    private void setUserInfo(MinimalUserInfo userInfo) {
        if (userInfo == null) {
            return;
        }
        RequestExtendParamHolder.set(ApplicationConstants.Security.ACCESS_TOKEN_USER_INFO_KEY, userInfo);
    }

    private void setShopInfo(MinimalShopInfo shopInfo) {
        if (shopInfo == null) {
            return;
        }
        RequestExtendParamHolder.set(ApplicationConstants.Security.ACCESS_TOKEN_SHOP_INFO_KEY, shopInfo);
    }

    /**
     * 获取商铺信息
     *
     * @param accessToken AccessTokenHeader
     * @return 商铺信息
     */
    private MinimalShopInfo obtainShopInfo(String accessToken) {
        if (accessToken == null) {
            return null;
        }
        if (accessToken.startsWith(ApplicationConstants.Security.TOKEN_SHOP_PREFIX)) {
            String token = getTokenString(accessToken, ApplicationConstants.Security.TOKEN_SHOP_PREFIX);
            try {
                return encoder.decode(token, MinimalShopInfo.class);
            } catch (Exception e) {
                log.debug("Unable to unpack access token, {}", e.getMessage());
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 获取用户信息
     *
     * @param accessToken AccessTokenHeader
     * @return 商铺信息
     */
    private MinimalUserInfo obtainUserInfo(String accessToken) {
        if (accessToken == null) {
            return null;
        }
        if (accessToken.startsWith(ApplicationConstants.Security.TOKEN_USER_PREFIX)) {
            String token = getTokenString(accessToken, ApplicationConstants.Security.TOKEN_USER_PREFIX);
            try {
                return encoder.decode(token, MinimalUserInfo.class);
            } catch (Exception e) {
                log.debug("Unable to unpack access token, {}", e.getMessage());
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 获取Token字符串
     *
     * @param tokenHeader 请求头
     * @param prefix      前缀
     * @return token
     */
    private String getTokenString(String tokenHeader, String prefix) {
        return tokenHeader.substring(prefix.length());
    }
}
